/*
 * CVideoDecoder.cpp
 *
 *  Created on: 2016年4月14日
 *      Author: terry
 */

#include "CVideoDecoder.h"
#include "FfmpegUtil.h"


namespace av
{

CVideoDecoder::CVideoDecoder(MediaDecoderSink* sink):
		m_sink(sink),
		m_codecCtx()
{

}

CVideoDecoder::~CVideoDecoder()
{
	close();
}

int CVideoDecoder::open(const MediaFormat& fmt)
{
	AVCodec* pCodec = avcodec_find_decoder((AVCodecID)fmt.m_codec);
	if (!pCodec)
	{
		return EBADF;
	}

	m_codecCtx = avcodec_alloc_context3(pCodec);

	m_codecCtx->refcounted_frames = 1;
	m_codecCtx->codec_type = AVMEDIA_TYPE_VIDEO;

	if (fmt.m_videoProp.size())
	{
		m_codecCtx->extradata = (uint8_t*)av_memdup(fmt.m_videoProp.c_str(), fmt.m_videoProp.size());
		m_codecCtx->extradata_size = fmt.m_videoProp.size();
	}

	if (avcodec_open2(m_codecCtx, pCodec, NULL) < 0)
	{
		avcodec_close(m_codecCtx);
		av_free(m_codecCtx);
		m_codecCtx = NULL;
		return EBADF;
	}

	start();

	return 0;
}

void CVideoDecoder::close()
{
	stop();

	if (m_codecCtx)
	{
		avcodec_close(m_codecCtx);
		av_free(m_codecCtx);
		m_codecCtx = NULL;
	}
}

bool CVideoDecoder::isOpen()
{
	return (m_codecCtx != NULL);
}

int CVideoDecoder::input(AVPacketPtr pkt)
{
	if (!pkt)
	{
		return EINVAL;
	}

	m_pktQueue.push(pkt);

	return 0;
}

void CVideoDecoder::setSink(MediaDecoderSink* sink)
{
	m_sink = sink;
}

void CVideoDecoder::flush()
{
	m_pktQueue.clear();
}

void CVideoDecoder::fireFrame(AVFramePtr frame)
{
	if (m_sink)
	{
		m_sink->onDecodeFrame(frame);
	}
}


int CVideoDecoder::run()
{
	while (!m_canExit)
	{
		AVPacketPtr pkt = waitAndGet(1000);
		if (!pkt)
		{
			continue;
		}

		AVFramePtr frame = decode(pkt);
		if (!frame)
		{
			continue;
		}

		fireFrame(frame);
	}
	return 0;
}

void CVideoDecoder::doStop()
{
	m_pktQueue.cancelWait();
}

AVPacketPtr CVideoDecoder::waitAndGet(int ms)
{
	AVPacketPtr pkt;
	m_pktQueue.pop(pkt);
	if (!pkt)
	{
		if (m_pktQueue.timedwait(ms))
		{
			m_pktQueue.pop(pkt);
		}
	}
	return pkt;
}

AVFramePtr CVideoDecoder::decode(AVPacketPtr& pkt)
{
	AVFramePtr frame(av_frame_alloc(), ffmpeg::AVFrameDeleter());
	int gotPicture = 0;
	int err = avcodec_decode_video2(m_codecCtx, frame.get(), &gotPicture, pkt.get());
	if (gotPicture)
	{
		return frame;
	}
	return AVFramePtr();
}

bool CVideoDecoder::getOutFormat(RawMediaFormat& fmt)
{
	if (!m_codecCtx)
	{
		return false;
	}

	fmt.m_width = m_codecCtx->width;
	fmt.m_height = m_codecCtx->height;
	fmt.m_pixelFormat = m_codecCtx->pix_fmt;
	if (m_codecCtx->framerate.den > 0)
	{
		fmt.m_framerate = m_codecCtx->framerate.num / m_codecCtx->framerate.den;
	}

	return true;
}



} /* namespace av */
